package com.easy.frame.servers.sys.service.impl.role;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.easy.frame.base.api.pojo.PageDTO;
import com.easy.frame.base.api.pojo.PageVO;
import com.easy.frame.base.basic.ServiceResponse;
import com.easy.frame.base.basic.constant.DbConstants;
import com.easy.frame.base.basic.enums.ResponseCodeEnum;
import com.easy.frame.base.basic.util.AuthUtils;
import com.easy.frame.base.service.mp.page.PageProxy;
import com.easy.frame.servers.sys.dto.SysRoleDTO;
import com.easy.frame.servers.sys.entity.SysRole;
import com.easy.frame.servers.sys.entity.SysRoleMenu;
import com.easy.frame.servers.sys.entity.SysStaffMenu;
import com.easy.frame.servers.sys.entity.SysStaffRole;
import com.easy.frame.servers.sys.mapper.role.SysRoleMapper;
import com.easy.frame.servers.sys.service.role.SysRoleMenuService;
import com.easy.frame.servers.sys.service.role.SysRoleService;
import com.easy.frame.servers.sys.service.staff.SysStaffMenuService;
import com.easy.frame.servers.sys.service.staff.SysStaffRoleService;
import com.easy.frame.servers.sys.util.RedisServer4Role;
import com.easy.frame.servers.sys.vo.SysRoleVO;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 系统角色表 服务实现类
 *
 * @author zzf
 * @since 2020-12-18
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

    @Resource
    SysRoleMenuService roleMenuService;

    @Resource
    SysStaffMenuService staffMenuService;

    @Resource
    SysStaffRoleService staffRoleService;

    @Resource
    SysRoleMapper sysRoleMapper;

    @Override
    public PageVO<SysRoleVO> selectForPage(PageDTO<SysRoleDTO> pageDTO) {
        if ("name".equals(pageDTO.getSortName())) {
            pageDTO.setSortName("CONVERT( roleName USING gbk )");
        }
        PageVO<SysRoleVO> proxy = PageProxy.proxy(baseMapper::selectForPage, pageDTO);
        List<SysRoleVO> records = proxy.getRecords();
        ServiceResponse<List<SysRoleVO>> success = new ServiceResponse<>(records, ResponseCodeEnum.SUCCESS);
        proxy.setRecords(success.getData());
        return proxy;
    }

    @Override
    public List<SysRoleVO> list(SysRoleDTO queryDTO) {
        return baseMapper.selectForPage(queryDTO);
    }

    @Override
    public SysRoleVO selectById(Long id) {
        return sysRoleMapper.selectVOById(id);
    }

    @Override
    public int updateDeleted(Long id) {
        SysRole role = new SysRole();
        role.setId(id);
        role.setDeleted(DbConstants.DEFAULT_YES);
        return baseMapper.updateById(role);
    }

    @Override
    public SysRoleVO selectSysById(Long id) {
        return sysRoleMapper.selectSysById(id);
    }

    @Override
    public int saveSysRole(SysRole entity) {
        if (entity.getTenantId() != null && entity.getTenantId() != 0) {
            entity.setTenantId(AuthUtils.getCurrentUserTenant());
        }
        entity.setCreateBy(AuthUtils.getCurrentUserId());
        entity.setUpdateBy(AuthUtils.getCurrentUserId());
        entity.setCreateTime(new Date());
        return sysRoleMapper.insert(entity);
    }

    @Override
    public List<SysRoleVO> selectSysByTenantId(Long tenantId) {
        return sysRoleMapper.selectSysByTenantId(tenantId);
    }

    @Override
    public void updateEntityById(SysRole entity) {
        entity.setUpdateBy(AuthUtils.getCurrentUserId());
        entity.setUpdateTime(new Date());
        updateById(entity);
    }

    @Override
    public void multiAuth(Long roleId, List<Long> staffIdList) {
        multiAuthAsync(Collections.singletonList(roleId), staffIdList, AuthUtils.getCurrentUserId());
    }

    /**
     * 使用最简单的先全部删除，再重新添加的方式。
     * 角色绑定菜单，因此修改角色时会同步修改用户的菜单。同时如果某用户存在单独授权菜单的将会失效。
     * 修改过的用户信息缓存将被清空，因此需要重新登录。
     *
     * @param roleIdList    角色id集合
     * @param staffIdList   用户id集合
     * @param currentUserId 当前用户id，异步无法获取原线程上下文，因此无法获取当前用户id，需手动传入
     */
    @Async
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void multiAuthAsync(List<Long> roleIdList, List<Long> staffIdList, final Long currentUserId) {
        //全部删除
        staffRoleService.remove(new LambdaQueryWrapper<SysStaffRole>()
                .in(SysStaffRole::getStaffId, staffIdList)
        );
        staffMenuService.remove(new LambdaQueryWrapper<SysStaffMenu>()
                .in(SysStaffMenu::getStaffId, staffIdList)
        );

        //遍历添加
        SysStaffRole staffRole = new SysStaffRole();
        staffRole.setCreateBy(currentUserId);
        SysStaffMenu staffMenu = new SysStaffMenu();
        staffMenu.setCreateBy(currentUserId);
        staffIdList.forEach(staffId -> {
            //添加员工菜单权限
            List<SysRoleMenu> roleMenuList = roleMenuService.list(new LambdaQueryWrapper<SysRoleMenu>()
                    .in(SysRoleMenu::getRoleId, roleIdList)
            );
            //查询出来后将list转成map
            roleMenuList.stream().collect(
                    Collectors.toMap(
                            //key
                            SysRoleMenu::getMenuId,
                            //value
                            SysRoleMenu::getOperations,
                            //value聚合时的操作
                            (value1, value2) -> {
                                value1.addAll(value2);
                                return value1;
                            }
                    )
                    //遍历map
            ).forEach((menuId, operationList) -> {
                staffMenu.setMenuId(menuId);
                staffMenu.setOperations(operationList.stream().distinct().collect(Collectors.toList()));
                staffMenuService.save(staffMenu);
            });
            //添加员工角色关联关系
            roleIdList.forEach(roleId -> {
                staffRole.setRoleId(roleId);
                staffRole.setStaffId(staffId);
                staffRoleService.save(staffRole);
            });
            //清空缓存
            RedisServer4Role.clearUserCache(staffId);
        });
    }
}
